#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область СлужебныеПроцедурыИФункции

////////////////////////////////////////////////////////////////////////////////
// Интерактивное удаление помеченных.

// Удаляет помеченные объекты, используется при интерактивном удалении в фоновом задании.
//
// Параметры:
//   ПараметрыВыполнения - Структура - Параметры, необходимые для удаления.
//   АдресХранилища - Строка - Адрес временного хранилища.
//
Процедура УдалитьПомеченныеОбъектыИнтерактивно(ПараметрыВыполнения, АдресХранилища) Экспорт
	УдалитьПомеченныеОбъекты(ПараметрыВыполнения);
	ПоместитьВоВременноеХранилище(ИзвлечьРезультат(ПараметрыВыполнения), АдресХранилища);
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Удаление помеченных из регламентного задания.

// Удаляет помеченные объекты из регламентного задания.
Процедура УдалитьПомеченныеОбъектыИзРегламентногоЗадания() Экспорт

	ПараметрыВыполнения = Новый Структура;
	УдалитьПомеченныеОбъекты(ПараметрыВыполнения);

КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Инициализация и запуск.

// Основная механика удаления помеченных объектов.
Процедура УдалитьПомеченныеОбъекты(ПараметрыВыполнения)

	Если Не УИ_Пользователи.ЭтоПолноправныйПользователь() Тогда
		ВызватьИсключение НСтр("ru = 'Недостаточно прав для выполнения операции.'");
	КонецЕсли;

	ИнициализироватьПараметры(ПараметрыВыполнения);

	Если ПараметрыВыполнения.ИскатьПомеченные Тогда
		ПолучитьПомеченныеНаУдаление(ПараметрыВыполнения);
	КонецЕсли;

	Если Не ПараметрыВыполнения.УдалятьПомеченные Тогда
		Возврат;
	КонецЕсли;

	Если ПараметрыВыполнения.Интерактивное И ПараметрыВыполнения.ВсеПомеченныеНаУдаление.Количество() = 0 Тогда
		Возврат; // Не удалять технологические объекты при интерактивном запуске если нет пользовательских объектов.
	КонецЕсли;

	Если ПараметрыВыполнения.Монопольно Тогда
		УдалитьПомеченныеОбъектыМонопольно(ПараметрыВыполнения);
	Иначе // Не монопольно.
		УдалитьПомеченныеОбъектыКонкурентно(ПараметрыВыполнения);
	КонецЕсли;

КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Конкурентное удаление помеченных.

// Основная механика.
Процедура УдалитьПомеченныеОбъектыКонкурентно(ПараметрыВыполнения)
	УстановитьПривилегированныйРежим(Истина);
	
	// Удаление технологических объектов (которые создавались и помечались на удаление без участия пользователя).
	Если ПараметрыВыполнения.ТехнологическиеОбъекты <> Неопределено Тогда
		ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, "ТехнологическиеОбъекты");
		Для Каждого Ссылка Из ПараметрыВыполнения.ТехнологическиеОбъекты Цикл
			УдалитьСсылку(ПараметрыВыполнения, Ссылка); // Для технологических объектов результат не выводится.
			ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, "ТехнологическиеОбъекты");
		КонецЦикла;
	КонецЕсли;
	
	// Удаление помеченных на удаление.
	ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, "ПользовательскиеОбъекты");
	Для Каждого Ссылка Из ПараметрыВыполнения.ПользовательскиеОбъекты Цикл
		Результат = УдалитьСсылку(ПараметрыВыполнения, Ссылка);
		ЗарегистрироватьРезультатУдаления(ПараметрыВыполнения, Ссылка, Результат, "ПользовательскиеОбъекты");
		ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, "ПользовательскиеОбъекты");
	КонецЦикла;
	
	// Удаление цепочек (линейно связанных объектов).
	ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, "ПовторноУдаляемые");
	Пока ПараметрыВыполнения.ПовторноУдаляемые.Количество() > 0 Цикл
		Ссылка = ПараметрыВыполнения.ПовторноУдаляемые[0];
		ПараметрыВыполнения.ПовторноУдаляемые.Удалить(0);

		Результат = УдалитьСсылку(ПараметрыВыполнения, Ссылка);

		ЗарегистрироватьРезультатУдаления(ПараметрыВыполнения, Ссылка, Результат, "ПовторноУдаляемые");
		ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, "ПовторноУдаляемые");
	КонецЦикла;
	
	// Удаление циклов (кольцевых связей объектов).
	УдалитьОставшиесяОбъектыВОднойТранзакции(ПараметрыВыполнения);
	
	// Очистка от "спама".
	ОчиститьСвязиОтИсключенийПоискаСсылок(ПараметрыВыполнения);

КонецПроцедуры

// Удаление одиночного объекта с контролем результата и откатом транзакции в случае неудачи.
Функция УдалитьСсылку(ПараметрыВыполнения, Ссылка)
	Результат = Новый Структура; // Результат обрабатывается в ЗарегистрироватьРезультатУдаления().
	Результат.Вставить("Успех", Неопределено);
	Результат.Вставить("ИнформацияОбОшибке", Неопределено);
	Результат.Вставить("ПрепятствующиеУдалению", Неопределено);
	Результат.Вставить("ВложенныеИПодчиненныеОбъекты", Новый Массив);
	Результат.Вставить("Количество", 0);
	Результат.Вставить("Сообщения", Неопределено);

	Информация = СформироватьИнформациюОТипах(ПараметрыВыполнения, ТипЗнч(Ссылка));

	НачатьТранзакцию();
	Попытка
		ПопробоватьУдалитьСсылку(ПараметрыВыполнения, Ссылка, Информация, Результат);
		Если Результат.Успех Тогда
			ЗафиксироватьТранзакцию();
		Иначе
			ОтменитьТранзакцию();
		КонецЕсли;
	Исключение
		ОтменитьТранзакцию();
		Результат.Успех = Ложь;
		Результат.ИнформацияОбОшибке = ИнформацияОбОшибке();
	КонецПопытки;
	Результат.Сообщения = УИ_ДлительныеОперации.СообщенияПользователю(Истина);

	Если Не Результат.Успех Тогда
		ЗаписатьПредупреждение(Ссылка, Результат.ИнформацияОбОшибке);
	КонецЕсли;

	Если ТипЗнч(Результат.ПрепятствующиеУдалению) = Тип("ТаблицаЗначений") Тогда
		Результат.ПрепятствующиеУдалению.Колонки[0].Имя = "УдаляемыйСсылка";
		Результат.ПрепятствующиеУдалению.Колонки[1].Имя = "ОбнаруженныйСсылка";
		Результат.ПрепятствующиеУдалению.Колонки[2].Имя = "ОбнаруженныйМетаданные";
		Для Каждого ОбнаруженныйСсылка Из Результат.ВложенныеИПодчиненныеОбъекты Цикл
			СтрокаТаблицы = Результат.ПрепятствующиеУдалению.Добавить();
			СтрокаТаблицы.УдаляемыйСсылка        = Ссылка;
			СтрокаТаблицы.ОбнаруженныйСсылка     = ОбнаруженныйСсылка;
		КонецЦикла;
	КонецЕсли;

	Возврат Результат;
КонецФункции

// Механика удаления объекта и поиска ссылок.
Процедура ПопробоватьУдалитьСсылку(ПараметрыВыполнения, Ссылка, Информация, Результат)
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить(Информация.ПолноеИмя);
	ЭлементБлокировки.УстановитьЗначение("Ссылка", Ссылка);
	Блокировка.Заблокировать();

	Объект = Ссылка.ПолучитьОбъект();
	Если Объект = Неопределено Тогда
		Результат.Успех = Истина; // Объект уже удален.
		Возврат;
	КонецЕсли;
	Если Объект.ПометкаУдаления <> Истина Тогда
		Результат.Успех = Ложь;
		Результат.ИнформацияОбОшибке = НСтр("ru = 'Объект не помечен на удаление.'");
		Возврат;
	КонецЕсли;

	НайтиВложенныеИПодчиненныеОбъекты(ПараметрыВыполнения, Ссылка, Информация, Результат);

	Объект.Удалить();

	НайтиПрепятствующиеУдалению(ПараметрыВыполнения, Ссылка, Информация, Результат);

	Если Результат.Количество = 0 Тогда
		Результат.Успех = Истина;
	Иначе
		Результат.Успех = Ложь;
		Результат.ИнформацияОбОшибке = НСтр("ru = 'Объект используется в других объектах программы.'");
	КонецЕсли;
КонецПроцедуры

// Поиск ссылок вложенных и подчиненных (иерархия и связь по владельцу). Выполняется до удаления.
Процедура НайтиВложенныеИПодчиненныеОбъекты(ПараметрыВыполнения, Ссылка, Информация, Результат)

	Если Информация.Иерархический Тогда
		Запрос = Новый Запрос(Информация.ТекстЗапросаПоИерархии);
		Запрос.УстановитьПараметр("УдаляемыйСсылка", Ссылка);
		ВложенныеОбъекты = Запрос.Выполнить().Выгрузить();
		Для Каждого СтрокаТаблицы Из ВложенныеОбъекты Цикл
			Результат.ВложенныеИПодчиненныеОбъекты.Добавить(СтрокаТаблицы.Ссылка);
		КонецЦикла;
		Результат.Количество = Результат.Количество + ВложенныеОбъекты.Количество();
	КонецЕсли;

	Если Информация.ЕстьПодчиненные Тогда
		Запрос = Новый Запрос(Информация.ТекстЗапросаПоПодчиненным);
		Запрос.УстановитьПараметр("УдаляемыйСсылка", Ссылка);
		ПодчиненныеОбъекты = Запрос.Выполнить().Выгрузить();
		Для Каждого СтрокаТаблицы Из ПодчиненныеОбъекты Цикл
			Результат.ВложенныеИПодчиненныеОбъекты.Добавить(СтрокаТаблицы.Ссылка);
		КонецЦикла;
		Результат.Количество = Результат.Количество + ПодчиненныеОбъекты.Количество();
	КонецЕсли;

КонецПроцедуры

// Поиск ссылок путем сканирования всех таблиц. Выполняется после удаления.
Процедура НайтиПрепятствующиеУдалению(ПараметрыВыполнения, Ссылка, Информация, Результат)

	ПоискСсылок = Новый Массив;
	ПоискСсылок.Добавить(Ссылка);

	ПрепятствующиеУдалению = НайтиПоСсылкам(ПоискСсылок);
	
	// Пропуск ссылок из границ последовательности.
	Количество = ПрепятствующиеУдалению.Количество();
	ИмяКолонки = ПрепятствующиеУдалению.Колонки[1].Имя;
	Для Номер = 1 По Количество Цикл
		ОбратныйИндекс = Количество - Номер;
		СтрокаТаблицы = ПрепятствующиеУдалению[ОбратныйИндекс];
		ПрепятствующийСсылка = СтрокаТаблицы[ИмяКолонки];
		Если ПрепятствующийСсылка = Ссылка Или ДокументУжеУдален(ПараметрыВыполнения, ПрепятствующийСсылка) Тогда
			ПрепятствующиеУдалению.Удалить(СтрокаТаблицы);
		КонецЕсли;
	КонецЦикла;
	
	// Регистрация результата.
	Результат.ПрепятствующиеУдалению = ПрепятствующиеУдалению;
	Результат.Количество = Результат.Количество + Результат.ПрепятствующиеУдалению.Количество();

КонецПроцедуры

// Поиск ссылки документа в базе данных.
Функция ДокументУжеУдален(ПараметрыВыполнения, Ссылка)
	Если Ссылка = Неопределено Тогда
		Возврат Ложь; // Не документ.
	КонецЕсли;
	Информация = СформироватьИнформациюОТипах(ПараметрыВыполнения, ТипЗнч(Ссылка));
	Если Информация.Вид <> "ДОКУМЕНТ" Тогда
		Возврат Ложь; // Не документ.
	КонецЕсли;
	Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 1 1 ИЗ " + Информация.ПолноеИмя + " Где Ссылка = &Ссылка");
	Запрос.УстановитьПараметр("Ссылка", Ссылка);
	Возврат Запрос.Выполнить().Пустой();
КонецФункции

// Удаление циклов (кольцевых связей объектов).
Процедура УдалитьОставшиесяОбъектыВОднойТранзакции(ПараметрыВыполнения)
	
	// 1. Объекты, которые невозможно удалить, получаются путем определения неразрешимых связей.
	ОбъектыКоторыеНевозможноУдалить = Новый Массив;
	ВложенныеНеразрешимыеСвязи = Новый Массив;

	Для Каждого Препятствующий Из ПараметрыВыполнения.ПрепятствующиеУдалению Цикл
		// 1.1. Критерием для определения неразрешимых связей является тот факт, что объект, препятствующий удалению, 
		// не является регистром. В случае с регистрами предполагаем, что записи регистра будут удалены автоматически,
		// при удалении объекта в его ведущем измерении.
		Если Препятствующий.ОбнаруженныйТип <> Тип("Строка") И Не УИ_ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(
			Метаданные.НайтиПоТипу(Препятствующий.ОбнаруженныйТип)) Тогда
			Продолжить;
		КонецЕсли;
		// 1.2. А также то, что объект, препятствующий удалению, не отмечен для удаления.
		Если ПараметрыВыполнения.НеУдаленные.Найти(Препятствующий.ОбнаруженныйСсылка) = Неопределено
			И ОбъектыКоторыеНевозможноУдалить.Найти(Препятствующий.УдаляемыйСсылка) = Неопределено Тогда
			ОбъектыКоторыеНевозможноУдалить.Добавить(Препятствующий.УдаляемыйСсылка);
			Найденные = ПараметрыВыполнения.ПрепятствующиеУдалению.НайтиСтроки(Новый Структура("ОбнаруженныйСсылка",
				Препятствующий.УдаляемыйСсылка));
			ВложенныеНеразрешимыеСвязи.Добавить(Найденные);
		КонецЕсли;
	КонецЦикла;
	
	// 1.3. Далее при помощи массива ВложенныеНеразрешимыеСвязи
	// получаются неразрешимые подчиненные - "связи связей", "связи связей связей" и т.д...
	Индекс = 0;
	Пока Индекс < ВложенныеНеразрешимыеСвязи.Количество() Цикл
		Найденные = ВложенныеНеразрешимыеСвязи[Индекс];
		Индекс = Индекс + 1;
		Для Каждого Препятствующий Из Найденные Цикл
			Если ОбъектыКоторыеНевозможноУдалить.Найти(Препятствующий.УдаляемыйСсылка) = Неопределено Тогда
				ОбъектыКоторыеНевозможноУдалить.Добавить(Препятствующий.УдаляемыйСсылка);
				Найденные = ПараметрыВыполнения.ПрепятствующиеУдалению.НайтиСтроки(
					Новый Структура("ОбнаруженныйСсылка", Препятствующий.УдаляемыйСсылка));
				ВложенныеНеразрешимыеСвязи.Добавить(Найденные);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	// 2. Объекты, которые можно попробовать удалить в одной транзакции.
	//    = Массив удаляемых - Массив объектов, которые невозможно удалить.
	УдаляемыеОбъекты = Новый Массив;
	Для Каждого НеУдаленныйОбъект Из ПараметрыВыполнения.НеУдаленные Цикл
		Если ОбъектыКоторыеНевозможноУдалить.Найти(НеУдаленныйОбъект) = Неопределено Тогда
			УдаляемыеОбъекты.Добавить(НеУдаленныйОбъект);
		КонецЕсли;
	КонецЦикла;

	Количество = УдаляемыеОбъекты.Количество();
	Если Количество = 0 Тогда
		Возврат; // Нет объектов для удаления.
	КонецЕсли;
	
	// 3. Включение всех объектов в одну транзакцию и попытка удалить.
	Успех = Ложь;
	НачатьТранзакцию();
	Попытка
		Для Номер = 1 По Количество Цикл
			ОбратныйИндекс = Количество - Номер;
			НеУдаленныйОбъект = УдаляемыеОбъекты[ОбратныйИндекс];

			Информация = СформироватьИнформациюОТипах(ПараметрыВыполнения, ТипЗнч(НеУдаленныйОбъект));

			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить(Информация.ПолноеИмя);
			ЭлементБлокировки.УстановитьЗначение("Ссылка", НеУдаленныйОбъект);
			Блокировка.Заблокировать();

			Объект = НеУдаленныйОбъект.ПолучитьОбъект();
			Если Объект = Неопределено Тогда // Объект уже удален.
				Продолжить;
			КонецЕсли;
			Если Объект.ПометкаУдаления <> Истина Тогда
				УдаляемыеОбъекты.Удалить(ОбратныйИндекс); // Объект уже не помечен на удаление.
				Продолжить;
			КонецЕсли;

			Объект.Удалить();
		КонецЦикла;
		НеУдаленныйОбъект = Неопределено;

		Если УдаляемыеОбъекты.Количество() > 0 Тогда
			ПрепятствующиеУдалению = НайтиПоСсылкам(УдаляемыеОбъекты);

			ИмяКолонки = ПрепятствующиеУдалению.Колонки[1].Имя;
			Для Каждого НеУдаленныйОбъект Из УдаляемыеОбъекты Цикл
				ПоискНеПрепятствующих = Новый Структура(ИмяКолонки, НеУдаленныйОбъект);
				НеПрепятствующие = ПрепятствующиеУдалению.НайтиСтроки(ПоискНеПрепятствующих);
				Для Каждого Препятствующий Из НеПрепятствующие Цикл
					ПрепятствующиеУдалению.Удалить(Препятствующий);
				КонецЦикла;
			КонецЦикла;

			Если ПрепятствующиеУдалению.Количество() = 0 Тогда
				Успех = Истина;
			КонецЕсли;
		КонецЕсли;

		Если Успех Тогда
			ЗафиксироватьТранзакцию();
		Иначе
			ОтменитьТранзакцию();
		КонецЕсли;

	Исключение
		ОтменитьТранзакцию();
		Успех = Ложь;
		ЗаписатьПредупреждение(НеУдаленныйОбъект, ИнформацияОбОшибке());
	КонецПопытки;
	
	// 4. Регистрация результата (если успех).
	Если Успех Тогда
		Для Каждого НеУдаленныйОбъект Из УдаляемыеОбъекты Цикл
			// Регистрация ссылки в коллекции удаленных.
			Если ПараметрыВыполнения.Удаленные.Найти(НеУдаленныйОбъект) = Неопределено Тогда
				ПараметрыВыполнения.Удаленные.Добавить(НеУдаленныйОбъект);
			КонецЕсли;
			
			// Удаление ссылки из коллекции не удаленных.
			Индекс = ПараметрыВыполнения.НеУдаленные.Найти(НеУдаленныйОбъект);
			Если Индекс <> Неопределено Тогда
				ПараметрыВыполнения.НеУдаленные.Удалить(Индекс);
			КонецЕсли;
			
			// Очистка информации о связях "от" удаленных объектов.
			Найденные = ПараметрыВыполнения.ПрепятствующиеУдалению.НайтиСтроки(Новый Структура("УдаляемыйСсылка",
				НеУдаленныйОбъект));
			Для Каждого Препятствующий Из Найденные Цикл
				ПараметрыВыполнения.ПрепятствующиеУдалению.Удалить(Препятствующий);
			КонецЦикла;
			
			// Очистка информации о связях "к" удаленным объектам.
			Найденные = ПараметрыВыполнения.ПрепятствующиеУдалению.НайтиСтроки(Новый Структура("ОбнаруженныйСсылка",
				НеУдаленныйОбъект));
			Для Каждого Препятствующий Из Найденные Цикл
				ПараметрыВыполнения.ПрепятствующиеУдалению.Удалить(Препятствующий);
			КонецЦикла;
		КонецЦикла;
	КонецЕсли;
КонецПроцедуры

// Очистка причин неудаления объектов от исключений поиска ссылок.
//   Применяется при оперативном удалении помеченных для того, чтобы удалить из результата "дублирующие" связи.
Процедура ОчиститьСвязиОтИсключенийПоискаСсылок(ПараметрыВыполнения)
	Если Не ПараметрыВыполнения.Свойство("ИсключенияПоискаСсылок") Тогда
		ПараметрыВыполнения.Вставить("ИсключенияПоискаСсылок", УИ_ОбщегоНазначения.ИсключенияПоискаСсылок());
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("ИсключающиеПравила") Тогда
		ПараметрыВыполнения.Вставить("ИсключающиеПравила", Новый Соответствие); // Кэш правил исключений поиска.
	КонецЕсли;

	ОбъектыСНеИсключениями = Новый Соответствие;
	ОбъектыТолькоСИсключениями = Новый Соответствие;
	
	// Определение "спама".
	ПрепятствующиеУдалению = ПараметрыВыполнения.ПрепятствующиеУдалению;
	ПрепятствующиеУдалению.Колонки.Добавить("ЭтоИсключение", Новый ОписаниеТипов("Булево"));
	Для Каждого Причина Из ПрепятствующиеУдалению Цикл
		Если Причина.ОбнаруженныйТип <> Тип("Строка") Тогда
			ОбнаруженныйМетаданные = Метаданные.НайтиПоТипу(Причина.ОбнаруженныйТип);
			Причина.ЭтоИсключение = СвязьВИсключенияхПоискаСсылок(ПараметрыВыполнения, ОбнаруженныйМетаданные, Причина);
		КонецЕсли;
		Если Причина.ЭтоИсключение Тогда
			Если ОбъектыСНеИсключениями[Причина.УдаляемыйСсылка] = Неопределено Тогда
				ОбъектыТолькоСИсключениями.Вставить(Причина.УдаляемыйСсылка, Истина);
			КонецЕсли;
		Иначе
			ОбъектыСНеИсключениями.Вставить(Причина.УдаляемыйСсылка, Истина);
			ОбъектыТолькоСИсключениями.Удалить(Причина.УдаляемыйСсылка);
		КонецЕсли;
	КонецЦикла;
	
	// Если кроме "спама" ничего не осталось,
	// то это ситуация, когда разработчик забыл добавить авто-очистку при удалении объекта.
	// В таких редких случаях вместо вывода пустого списка причин, можно вывести "спам".
	Для Каждого КлючИЗначение Из ОбъектыТолькоСИсключениями Цикл
		Найденные = ПрепятствующиеУдалению.НайтиСтроки(Новый Структура("УдаляемыйСсылка", КлючИЗначение.Ключ));
		Для Каждого Причина Из Найденные Цикл
			Причина.ЭтоИсключение = Ложь;
		КонецЦикла;
	КонецЦикла;
	
	// Удаление "спама".
	Найденные = ПрепятствующиеУдалению.НайтиСтроки(Новый Структура("ЭтоИсключение", Истина));
	Для Каждого Причина Из Найденные Цикл
		ПрепятствующиеУдалению.Удалить(Причина);
	КонецЦикла;

	ПрепятствующиеУдалению.Колонки.Удалить("ЭтоИсключение");
	ПараметрыВыполнения.Удалить("ИсключенияПоискаСсылок");
	ПараметрыВыполнения.Удалить("ИсключающиеПравила");
КонецПроцедуры

// Регистрация результата удаления и заполнение коллекции ПовторноУдаляемые.
Процедура ЗарегистрироватьРезультатУдаления(ПараметрыВыполнения, Ссылка, Результат, ИмяКоллекции)
	// Результат формируется в УдалитьСсылку().
	Если Результат.Успех Тогда
		// Регистрация ссылки в коллекции удаленных.
		ПараметрыВыполнения.Удаленные.Добавить(Ссылка);
		
		// Исключение удаленного объекта из причин неудаления других объектов. Поиск.
		НеактуальныеПричины = ПараметрыВыполнения.ПрепятствующиеУдалению.НайтиСтроки(
			Новый Структура("ОбнаруженныйСсылка", Ссылка));
		Для Каждого Причина Из НеактуальныеПричины Цикл
			// Удаление причины неудаления другого объекта.
			УдаляемыйСсылка = Причина.УдаляемыйСсылка;
			ПараметрыВыполнения.ПрепятствующиеУдалению.Удалить(Причина);
			// Поиск других причин неудаления другого объекта.
			Если ПараметрыВыполнения.ПрепятствующиеУдалению.Найти(УдаляемыйСсылка, "УдаляемыйСсылка") = Неопределено Тогда
				// Устранены все причины неудаления другого объекта.
				// Регистрация другого объекта для повторного удаления.
				ПараметрыВыполнения.ПовторноУдаляемые.Добавить(УдаляемыйСсылка);
				Если ИмяКоллекции = "ПовторноУдаляемые" И ПараметрыВыполнения.Интерактивное Тогда
					ПараметрыВыполнения.Всего = ПараметрыВыполнения.Всего + 1;
				КонецЕсли;
				// Очистка записи о другом объекте из коллекции "НеУдаленные".
				Индекс = ПараметрыВыполнения.НеУдаленные.Найти(УдаляемыйСсылка);
				Если Индекс <> Неопределено Тогда
					ПараметрыВыполнения.НеУдаленные.Удалить(Индекс);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;

	Иначе // Не успешно.

		ПараметрыВыполнения.НеУдаленные.Добавить(Ссылка);

		Если ТипЗнч(Результат.ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке") Или Результат.ПрепятствующиеУдалению
			= Неопределено Тогда // Текст ошибки
			Если ТипЗнч(Результат.ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке") Тогда
				ТекстОшибки = КраткоеПредставлениеОшибки(Результат.ИнформацияОбОшибке);
				Подробно    = ПодробноеПредставлениеОшибки(Результат.ИнформацияОбОшибке);
			Иначе
				ТекстОшибки = Результат.ИнформацияОбОшибке;
				Подробно    = "";
			КонецЕсли;
			Для Каждого СообщениеОтОбъекта Из Результат.Сообщения Цикл
				ТекстОшибки = СокрП(ТекстОшибки + Символы.ПС + Символы.ПС + СокрЛ(СообщениеОтОбъекта.Текст));
				Подробно    = СокрП(Подробно + Символы.ПС + Символы.ПС + СокрЛ(СообщениеОтОбъекта.Текст));
			КонецЦикла;
			Причина = ПараметрыВыполнения.ПрепятствующиеУдалению.Добавить();
			Причина.УдаляемыйСсылка    = Ссылка;
			Причина.УдаляемыйТип       = ТипЗнч(Причина.УдаляемыйСсылка);
			Причина.ОбнаруженныйСсылка = ТекстОшибки;
			Причина.ОбнаруженныйТип    = Тип("Строка");
			Причина.Подробно           = Подробно;
			СформироватьИнформациюОТипах(ПараметрыВыполнения, Причина.УдаляемыйТип);
		Иначе // Регистрация связей (причин не удаления) для вывода пользователю.
			Для Каждого СтрокаТаблицы Из Результат.ПрепятствующиеУдалению Цикл
				ЗаписатьПричинуВРезультат(ПараметрыВыполнения, СтрокаТаблицы);
			КонецЦикла;
		КонецЕсли;

	КонецЕсли; // Результат.Успех.
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Монопольное удаление помеченных.

// Основная механика удаления помеченных объектов.
Процедура УдалитьПомеченныеОбъектыМонопольно(ПараметрыВыполнения)

	Если Не ПараметрыВыполнения.Свойство("ИсключенияПоискаСсылок") Тогда
		ПараметрыВыполнения.Вставить("ИсключенияПоискаСсылок", УИ_ОбщегоНазначения.ИсключенияПоискаСсылок());
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("ИсключающиеПравила") Тогда
		ПараметрыВыполнения.Вставить("ИсключающиеПравила", Новый Соответствие); // Кэш правил исключений поиска.
	КонецЕсли;

	УдаляемыеОбъекты = ПараметрыВыполнения.ВсеПомеченныеНаУдаление;

	Пока УдаляемыеОбъекты.Количество() > 0 Цикл

		ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, "МонопольноеУдаление");

		ПрепятствующиеУдалению = Новый ТаблицаЗначений;
		
		// Попытка удалить с контролем ссылочной целостности.
		УстановитьПривилегированныйРежим(Истина);
		УдалитьОбъекты(УдаляемыеОбъекты, Истина, ПрепятствующиеУдалению);
		УстановитьПривилегированныйРежим(Ложь);
		Если ПрепятствующиеУдалению.Колонки.Количество() < 3 Тогда
			ВызватьИсключение НСтр("ru = 'Не удалось выполнить удаление объектов.'");
		КонецЕсли;
		
		// Назначение имен колонок для таблицы конфликтов, возникших при удалении.
		ПрепятствующиеУдалению.Колонки[0].Имя = "УдаляемыйСсылка";
		ПрепятствующиеУдалению.Колонки[1].Имя = "ОбнаруженныйСсылка";
		ПрепятствующиеУдалению.Колонки[2].Имя = "ОбнаруженныйМетаданные";

		ВсеСвязиВИсключениях = Истина;
		
		// Анализ причин не удаления (мест использования помеченных на удаление).
		ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, "ПрепятствующиеУдалению", ПрепятствующиеУдалению);
		Для Каждого СтрокаТаблицы Из ПрепятствующиеУдалению Цикл
			ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, "ПрепятствующиеУдалению");
			
			// Проверка исключающих правил.
			Если СвязьВИсключенияхПоискаСсылок(ПараметрыВыполнения, СтрокаТаблицы.ОбнаруженныйМетаданные,
				СтрокаТаблицы) Тогда
				Продолжить; // Связь не препятствует удалению.
			КонецЕсли;
			
			// Невозможно удалить объект (мешает обнаруженная ссылка или запись регистра).
			ВсеСвязиВИсключениях = Ложь;
			
			// Сокращение удаляемых объектов.
			Индекс = УдаляемыеОбъекты.Найти(СтрокаТаблицы.УдаляемыйСсылка);
			Если Индекс <> Неопределено Тогда
				УдаляемыеОбъекты.Удалить(Индекс);
			КонецЕсли;
			
			// Регистрация связи для вывода пользователю.
			ЗаписатьПричинуВРезультат(ПараметрыВыполнения, СтрокаТаблицы);
		КонецЦикла;
		
		// Удаление без контроля, если все связи в исключениях поиска ссылок.
		Если ВсеСвязиВИсключениях Тогда
			УстановитьПривилегированныйРежим(Истина);
			УдалитьОбъекты(УдаляемыеОбъекты, Ложь);
			УстановитьПривилегированныйРежим(Ложь);
			Прервать; // Выход из цикла.
		КонецЕсли;
	КонецЦикла;

	ПараметрыВыполнения.Вставить("Удаленные", УдаляемыеОбъекты);
	ПараметрыВыполнения.Удалить("ИсключенияПоискаСсылок");
	ПараметрыВыполнения.Удалить("ИсключающиеПравила");

КонецПроцедуры

// Проверяет что связь в исключениях.
Функция СвязьВИсключенияхПоискаСсылок(ПараметрыВыполнения, ОбнаруженныйМетаданные, СтрокаТаблицы)
	// Определение исключающего правила для объекта метаданных, препятствующего удалению:
	// Для регистров (т.н. "необъектных таблиц") - массива реквизитов для поиска в записи регистра.
	// Для ссылочных типов (т.н. "объектных таблиц") - готового запроса для поиска в реквизитах.
	Правило = ПараметрыВыполнения.ИсключающиеПравила[ОбнаруженныйМетаданные]; // Кэш.
	Если Правило = Неопределено Тогда
		Правило = СформироватьИсключающееПравило(ПараметрыВыполнения, ОбнаруженныйМетаданные);
		ПараметрыВыполнения.ИсключающиеПравила.Вставить(ОбнаруженныйМетаданные, Правило);
	КонецЕсли;
	
	// Проверка исключающего правила.
	Если Правило = "*" Тогда
		Возврат Истина; // Можно удалять (обнаруженный объект метаданных не мешает).
	ИначеЕсли ТипЗнч(Правило) = Тип("Массив") Тогда // Имена измерений регистра.
		Для Каждого ИмяРеквизита Из Правило Цикл
			Если СтрокаТаблицы.ОбнаруженныйСсылка[ИмяРеквизита] = СтрокаТаблицы.УдаляемыйСсылка Тогда
				Возврат Истина; // Можно удалять (обнаруженная запись регистра не мешает).
			КонецЕсли;
		КонецЦикла;
	ИначеЕсли ТипЗнч(Правило) = Тип("Запрос") Тогда // Запрос к ссылочному объекту.
		Правило.УстановитьПараметр("УдаляемыйСсылка", СтрокаТаблицы.УдаляемыйСсылка);
		Правило.УстановитьПараметр("ОбнаруженныйСсылка", СтрокаТаблицы.ОбнаруженныйСсылка);
		Если Не Правило.Выполнить().Пустой() Тогда
			Возврат Истина; // Можно удалять (обнаруженная ссылка не мешает).
		КонецЕсли;
	КонецЕсли;

	Возврат Ложь;
КонецФункции

// Компонует правило оптимально для проверки.
Функция СформироватьИсключающееПравило(ПараметрыВыполнения, ОбнаруженныйМетаданные)
	ИсключениеПоиска = ПараметрыВыполнения.ИсключенияПоискаСсылок[ОбнаруженныйМетаданные];
	Если ИсключениеПоиска = "*" Тогда
		Возврат "*"; // Можно удалять (обнаруженный объект метаданных не мешает).
	КонецЕсли;
	
	// Формирование исключающего правила.
	ЭтоРегистрСведений = Метаданные.РегистрыСведений.Содержит(ОбнаруженныйМетаданные);
	Если ЭтоРегистрСведений Или Метаданные.РегистрыБухгалтерии.Содержит(ОбнаруженныйМетаданные) // ЭтоРегистрБухгалтерии

		Или Метаданные.РегистрыНакопления.Содержит(ОбнаруженныйМетаданные) Тогда // ЭтоРегистрНакопления

		Правило = Новый Массив;
		Если ЭтоРегистрСведений Тогда
			Для Каждого Измерение Из ОбнаруженныйМетаданные.Измерения Цикл
				Если Измерение.Ведущее Тогда
					Правило.Добавить(Измерение.Имя);
				КонецЕсли;
			КонецЦикла;
		Иначе
			Для Каждого Измерение Из ОбнаруженныйМетаданные.Измерения Цикл
				Правило.Добавить(Измерение.Имя);
			КонецЦикла;
		КонецЕсли;

		Если ТипЗнч(ИсключениеПоиска) = Тип("Массив") Тогда
			Для Каждого ИмяРеквизита Из ИсключениеПоиска Цикл
				Если Правило.Найти(ИмяРеквизита) = Неопределено Тогда
					Правило.Добавить(ИмяРеквизита);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;

	ИначеЕсли ТипЗнч(ИсключениеПоиска) = Тип("Массив") Тогда

		ТекстыЗапросов = Новый Соответствие;
		ИмяКорневойТаблицы = ОбнаруженныйМетаданные.ПолноеИмя();

		Для Каждого ПутьКРеквизиту Из ИсключениеПоиска Цикл
			ПозицияТочки = СтрНайти(ПутьКРеквизиту, ".");
			Если ПозицияТочки = 0 Тогда
				ПолноеИмяТаблицы = ИмяКорневойТаблицы;
				ИмяРеквизита = ПутьКРеквизиту;
			Иначе
				ПолноеИмяТаблицы = ИмяКорневойТаблицы + "." + Лев(ПутьКРеквизиту, ПозицияТочки - 1);
				ИмяРеквизита = Сред(ПутьКРеквизиту, ПозицияТочки + 1);
			КонецЕсли;

			ТекстВложенногоЗапроса = ТекстыЗапросов.Получить(ПолноеИмяТаблицы);
			Если ТекстВложенногоЗапроса = Неопределено Тогда
				ТекстВложенногоЗапроса = "ВЫБРАТЬ ПЕРВЫЕ 1
										 |	1
										 |ИЗ
										 |	" + ПолноеИмяТаблицы + " КАК Таблица
																	 |ГДЕ
																	 |	Таблица.Ссылка = &ОбнаруженныйСсылка
																	 |	И (";
			Иначе
				ТекстВложенногоЗапроса = ТекстВложенногоЗапроса + Символы.ПС + Символы.Таб + Символы.Таб + "ИЛИ ";
			КонецЕсли;
			ТекстВложенногоЗапроса = ТекстВложенногоЗапроса + "Таблица." + ИмяРеквизита + " = &УдаляемыйСсылка";

			ТекстыЗапросов.Вставить(ПолноеИмяТаблицы, ТекстВложенногоЗапроса);
		КонецЦикла;

		ТекстЗапроса = "";
		Для Каждого КлючИЗначение Из ТекстыЗапросов Цикл
			Если ТекстЗапроса <> "" Тогда
				ТекстЗапроса = ТекстЗапроса + Символы.ПС + Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС + Символы.ПС;
			КонецЕсли;
			ТекстЗапроса = ТекстЗапроса + КлючИЗначение.Значение + ")";
		КонецЦикла;

		Правило = Новый Запрос;
		Правило.Текст = ТекстЗапроса;

	Иначе

		Правило = "";

	КонецЕсли;

	Возврат Правило;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Общая механика.

// Инициализирует структуру параметров, необходимых для выполнения других служебных методов.
Процедура ИнициализироватьПараметры(ПараметрыВыполнения)
	// Определение параметров работы программы.
	Если Не ПараметрыВыполнения.Свойство("ИскатьПомеченные") Тогда
		ПараметрыВыполнения.Вставить("ИскатьПомеченные", Истина);
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("УдалятьПомеченные") Тогда
		ПараметрыВыполнения.Вставить("УдалятьПомеченные", Истина);
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("Монопольно") Тогда
		ПараметрыВыполнения.Вставить("Монопольно", Ложь);
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("ТехнологическиеОбъекты") Тогда
		ПараметрыВыполнения.Вставить("ТехнологическиеОбъекты", Новый Массив);
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("ПользовательскиеОбъекты") Тогда
		ПараметрыВыполнения.Вставить("ПользовательскиеОбъекты", Новый Массив);
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("ВсеПомеченныеНаУдаление") Тогда
		ПараметрыВыполнения.Вставить("ВсеПомеченныеНаУдаление", Новый Массив);
		УИ_ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ПараметрыВыполнения.ВсеПомеченныеНаУдаление,
			ПараметрыВыполнения.ТехнологическиеОбъекты);
		УИ_ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ПараметрыВыполнения.ВсеПомеченныеНаУдаление,
			ПараметрыВыполнения.ПользовательскиеОбъекты);
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("МодельСервиса") Тогда
		ПараметрыВыполнения.Вставить("МодельСервиса", УИ_ОбщегоНазначения.РазделениеВключено());
		Если ПараметрыВыполнения.МодельСервиса Тогда
//			Если УИ_ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса") Тогда
//				МодульРаботаВМоделиСервиса = УИ_ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
//				РазделительОсновныхДанных = МодульРаботаВМоделиСервиса.РазделительОсновныхДанных();
//				РазделительВспомогательныхДанных = МодульРаботаВМоделиСервиса.РазделительВспомогательныхДанных();
//			Иначе
				РазделительОсновныхДанных = Неопределено;
				РазделительВспомогательныхДанных = Неопределено;
//			КонецЕсли;

			ПараметрыВыполнения.Вставить("ВОбластиДанных", УИ_ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных());
			ПараметрыВыполнения.Вставить("РазделительОсновныхДанных", РазделительОсновныхДанных);
			ПараметрыВыполнения.Вставить("РазделительВспомогательныхДанных", РазделительВспомогательныхДанных);
		КонецЕсли;
	КонецЕсли;
	Если Не ПараметрыВыполнения.Свойство("ИнформацияОТипах") Тогда
		ПараметрыВыполнения.Вставить("ИнформацияОТипах", Новый Соответствие);
	КонецЕсли;

	ПрепятствующиеУдалению = Новый ТаблицаЗначений;
	ПрепятствующиеУдалению.Колонки.Добавить("УдаляемыйСсылка");
	ПрепятствующиеУдалению.Колонки.Добавить("УдаляемыйТип", Новый ОписаниеТипов("Тип"));
	ПрепятствующиеУдалению.Колонки.Добавить("ОбнаруженныйСсылка");
	ПрепятствующиеУдалению.Колонки.Добавить("ОбнаруженныйТип", Новый ОписаниеТипов("Тип"));
	ПрепятствующиеУдалению.Колонки.Добавить("ОбнаруженныйПометкаУдаления", Новый ОписаниеТипов("Булево"));
	ПрепятствующиеУдалению.Колонки.Добавить("Подробно", Новый ОписаниеТипов("Строка"));

	ПрепятствующиеУдалению.Индексы.Добавить("УдаляемыйСсылка");
	ПрепятствующиеУдалению.Индексы.Добавить("ОбнаруженныйСсылка");

	ПараметрыВыполнения.Вставить("Удаленные", Новый Массив);
	ПараметрыВыполнения.Вставить("НеУдаленные", Новый Массив);
	ПараметрыВыполнения.Вставить("ПрепятствующиеУдалению", ПрепятствующиеУдалению);
	ПараметрыВыполнения.Вставить("ПовторноУдаляемые", Новый Массив);
	ПараметрыВыполнения.Вставить("Интерактивное", ПараметрыВыполнения.Свойство("ПериодЗаписи"));

	ИнициализироватьПараметрыДляРегистрацииПрогресса(ПараметрыВыполнения);
КонецПроцедуры

// Формирует массив помеченных на удаление с учетом разделения.
Процедура ПолучитьПомеченныеНаУдаление(ПараметрыВыполнения)

	ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, "ПередПоискомПомеченныхНаУдаление");
//	МодульУдалениеПомеченныхОбъектовПереопределяемый=УИ_ОбщегоНазначения.ОбщийМодуль(
//		"УдалениеПомеченныхОбъектовПереопределяемый");
//	Если МодульУдалениеПомеченныхОбъектовПереопределяемый <> Неопределено Тогда
//		МодульУдалениеПомеченныхОбъектовПереопределяемый.ПередПоискомПомеченныхНаУдаление(ПараметрыВыполнения);
//	КонецЕсли;

	УстановитьПривилегированныйРежим(Истина);
	
	// Получение списка помеченных на удаление.
	ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, "ПоискПомеченныхНаУдаление");
	ПараметрыВыполнения.ВсеПомеченныеНаУдаление = НайтиПомеченныеНаУдаление();
	
	// Распределение помеченных на удаление по коллекциям.
	ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, "ВсеПомеченныеНаУдаление");
	Количество = ПараметрыВыполнения.ВсеПомеченныеНаУдаление.Количество();
	Для Номер = 1 По Количество Цикл
		ОбратныйИндекс = Количество - Номер;
		Ссылка = ПараметрыВыполнения.ВсеПомеченныеНаУдаление[ОбратныйИндекс];

		Информация = СформироватьИнформациюОТипах(ПараметрыВыполнения, ТипЗнч(Ссылка));
		ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, "ВсеПомеченныеНаУдаление");

		Если ПараметрыВыполнения.МодельСервиса И ПараметрыВыполнения.ВОбластиДанных И Не Информация.Разделенный Тогда
			ПараметрыВыполнения.ВсеПомеченныеНаУдаление.Удалить(ОбратныйИндекс);
			Продолжить; // Неразделенные объекты запрещено изменять из области данных.
		КонецЕсли;

		Если Информация.ЕстьПредопределенные И Информация.Предопределенные.Найти(Ссылка) <> Неопределено Тогда
			ПараметрыВыполнения.ВсеПомеченныеНаУдаление.Удалить(ОбратныйИндекс);
			Продолжить; // Предопределенные элементы создаются и удаляются только автоматически.
		КонецЕсли;

		Если Информация.Технический = Истина Тогда
			ПараметрыВыполнения.ТехнологическиеОбъекты.Добавить(Ссылка);
		Иначе
			ПараметрыВыполнения.ПользовательскиеОбъекты.Добавить(Ссылка);
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

// Формирует информацию о типе объекта метаданных, как то: полное имя, представления, вид и т.п.
Функция СформироватьИнформациюОТипах(ПараметрыВыполнения, Тип) Экспорт
	Информация = ПараметрыВыполнения.ИнформацияОТипах.Получить(Тип); // Кэш.
	Если Информация <> Неопределено Тогда
		Возврат Информация;
	КонецЕсли;

	Информация = Новый Структура("ПолноеИмя, ПредставлениеЭлемента, ПредставлениеСписка,
								 |Вид, Ссылочный, Технический, Разделенный,
								 |Иерархический, ТекстЗапросаПоИерархии,
								 |ЕстьПодчиненные, ТекстЗапросаПоПодчиненным,
								 |ЕстьПредопределенные, Предопределенные");
	
	// Поиск объекта метаданных.
	ОбъектМетаданных = Метаданные.НайтиПоТипу(Тип);
	
	// Заполнение базовой информации.
	Информация.ПолноеИмя = ВРег(ОбъектМетаданных.ПолноеИмя());
	
	// Представления: элемента и списка.
	СтандартныеСвойства = Новый Структура("ПредставлениеОбъекта, РасширенноеПредставлениеОбъекта, ПредставлениеСписка, РасширенноеПредставлениеСписка");
	ЗаполнитьЗначенияСвойств(СтандартныеСвойства, ОбъектМетаданных);
	Если ЗначениеЗаполнено(СтандартныеСвойства.ПредставлениеОбъекта) Тогда
		Информация.ПредставлениеЭлемента = СтандартныеСвойства.ПредставлениеОбъекта;
	ИначеЕсли ЗначениеЗаполнено(СтандартныеСвойства.РасширенноеПредставлениеОбъекта) Тогда
		Информация.ПредставлениеЭлемента = СтандартныеСвойства.РасширенноеПредставлениеОбъекта;
	Иначе
		Информация.ПредставлениеЭлемента = ОбъектМетаданных.Представление();
	КонецЕсли;
	Если ЗначениеЗаполнено(СтандартныеСвойства.ПредставлениеСписка) Тогда
		Информация.ПредставлениеСписка = СтандартныеСвойства.ПредставлениеСписка;
	ИначеЕсли ЗначениеЗаполнено(СтандартныеСвойства.РасширенноеПредставлениеСписка) Тогда
		Информация.ПредставлениеСписка = СтандартныеСвойства.РасширенноеПредставлениеСписка;
	Иначе
		Информация.ПредставлениеСписка = ОбъектМетаданных.Представление();
	КонецЕсли;
	
	// Вид и его свойства.
	Информация.Вид = Лев(Информация.ПолноеИмя, СтрНайти(Информация.ПолноеИмя, ".") - 1);
	Если Информация.Вид = "СПРАВОЧНИК" Или Информация.Вид = "ДОКУМЕНТ" Или Информация.Вид = "ПЕРЕЧИСЛЕНИЕ"
		Или Информация.Вид = "ПЛАНВИДОВХАРАКТЕРИСТИК" Или Информация.Вид = "ПЛАНСЧЕТОВ" Или Информация.Вид = "ПЛАНВИДОВРАСЧЕТА"
		Или Информация.Вид = "БИЗНЕСПРОЦЕСС" Или Информация.Вид = "ЗАДАЧА" Или Информация.Вид = "ПЛАНОБМЕНА" Тогда
		Информация.Ссылочный = Истина;
	Иначе
		Информация.Ссылочный = Ложь;
	КонецЕсли;

	Если Информация.Вид = "СПРАВОЧНИК" Или Информация.Вид = "ПЛАНВИДОВХАРАКТЕРИСТИК" Тогда
		Информация.Иерархический = ОбъектМетаданных.Иерархический;
	ИначеЕсли Информация.Вид = "ПЛАНСЧЕТОВ" Тогда
		Информация.Иерархический = Истина;
	Иначе
		Информация.Иерархический = Ложь;
	КонецЕсли;
	Если Информация.Иерархический Тогда
		ШаблонЗапроса = "ВЫБРАТЬ Ссылка ИЗ &ПолноеИмя ГДЕ Родитель = &УдаляемыйСсылка";
		Информация.ТекстЗапросаПоИерархии = СтрЗаменить(ШаблонЗапроса, "&ПолноеИмя", Информация.ПолноеИмя);
	КонецЕсли;

	Информация.ЕстьПодчиненные = Ложь;
	Информация.ТекстЗапросаПоПодчиненным = "";
	Если Информация.Вид = "СПРАВОЧНИК" Или Информация.Вид = "ПЛАНВИДОВХАРАКТЕРИСТИК" Или Информация.Вид = "ПЛАНОБМЕНА"
		Или Информация.Вид = "ПЛАНСЧЕТОВ" Или Информация.Вид = "ПЛАНВИДОВРАСЧЕТА" Тогда

		ШаблонЗапроса = "ВЫБРАТЬ Ссылка ИЗ Справочник.&Имя ГДЕ Владелец = &УдаляемыйСсылка";
		ТекстЗапроса = "";

		Для Каждого Справочник Из Метаданные.Справочники Цикл
			Если Справочник.Владельцы.Содержит(ОбъектМетаданных) Тогда
				Если Информация.ЕстьПодчиненные = Ложь Тогда
					Информация.ЕстьПодчиненные = Истина;
				Иначе
					ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС;
				КонецЕсли;
				ТекстЗапроса = ТекстЗапроса + СтрЗаменить(ШаблонЗапроса, "&Имя", Справочник.Имя);
			КонецЕсли;
		КонецЦикла;

		Информация.ТекстЗапросаПоПодчиненным = ТекстЗапроса;
	КонецЕсли;

	Информация.Технический = ЭтоТехническийОбъект(Информация.ПолноеИмя);
	Если ПараметрыВыполнения.МодельСервиса Тогда

//		Если УИ_ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса") Тогда
//			МодульРаботаВМоделиСервиса = УИ_ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
//			ЭтоРазделенныйОбъектМетаданных = МодульРаботаВМоделиСервиса.ЭтоРазделенныйОбъектМетаданных(ОбъектМетаданных);
//		Иначе
			ЭтоРазделенныйОбъектМетаданных = Ложь;
//		КонецЕсли;

		Информация.Разделенный = ЭтоРазделенныйОбъектМетаданных;

	КонецЕсли;

	Если Информация.Вид = "СПРАВОЧНИК" Или Информация.Вид = "ПЛАНВИДОВХАРАКТЕРИСТИК" Или Информация.Вид = "ПЛАНСЧЕТОВ"
		Или Информация.Вид = "ПЛАНВИДОВРАСЧЕТА" Тогда
		Запрос = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ " + Информация.ПолноеИмя + " ГДЕ Предопределенный И ПометкаУдаления");
		Информация.Предопределенные = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
		Информация.ЕстьПредопределенные = Информация.Предопределенные.Количество() > 0;
	Иначе
		Информация.ЕстьПредопределенные = Ложь;
	КонецЕсли;

	ПараметрыВыполнения.ИнформацияОТипах.Вставить(Тип, Информация);

	Возврат Информация;
КонецФункции

Функция ЭтоТехническийОбъект(Знач ПолноеИмяОбъекта)
	Возврат ПолноеИмяОбъекта = "СПРАВОЧНИК.ИДЕНТИФИКАТОРЫОБЪЕКТОВМЕТАДАННЫХ" Или ПолноеИмяОбъекта = "СПРАВОЧНИК.ПРЕДОПРЕДЕЛЕННЫЕВАРИАНТЫОТЧЕТОВ"
		Или ПолноеИмяОбъекта = "СПРАВОЧНИК.ИДЕНТИФИКАТОРЫОБЪЕКТОВРАСШИРЕНИЙ" Или ПолноеИмяОбъекта
		= "СПРАВОЧНИК.ПРЕДОПРЕДЕЛЕННЫЕВАРИАНТЫОТЧЕТОВРАСШИРЕНИЙ";
КонецФункции

// Регистрирует предупреждение в журнале регистрации.
Процедура ЗаписатьПредупреждение(Ссылка, ИнформацияОбОшибке)
	Если ТипЗнч(ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке") Тогда
		ТекстДляЖурнала = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
	Иначе
		ТекстДляЖурнала = ИнформацияОбОшибке;
	КонецЕсли;

	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Удаление помеченных'", УИ_ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Предупреждение, , Ссылка, ТекстДляЖурнала);
КонецПроцедуры

// Регистрация причины неудаления.
Процедура ЗаписатьПричинуВРезультат(ПараметрыВыполнения, СтрокаТаблицы)
	УдаляемыйТип        = ТипЗнч(СтрокаТаблицы.УдаляемыйСсылка);
	УдаляемыйИнформация = СформироватьИнформациюОТипах(ПараметрыВыполнения, УдаляемыйТип);
	Если УдаляемыйИнформация.Технический Тогда
		Возврат;
	КонецЕсли;
	
	// Добавление не удаленных объектов.
	Если ПараметрыВыполнения.НеУдаленные.Найти(СтрокаТаблицы.УдаляемыйСсылка) = Неопределено Тогда
		ПараметрыВыполнения.НеУдаленные.Добавить(СтрокаТаблицы.УдаляемыйСсылка);
	КонецЕсли;

	Причина = ПараметрыВыполнения.ПрепятствующиеУдалению.Добавить();
	ЗаполнитьЗначенияСвойств(Причина, СтрокаТаблицы);
	Причина.УдаляемыйТип    = УдаляемыйТип;
	Причина.ОбнаруженныйТип = ТипЗнч(Причина.ОбнаруженныйСсылка);

	Если СтрокаТаблицы.ОбнаруженныйСсылка = Неопределено Тогда
		Если Метаданные.Константы.Содержит(СтрокаТаблицы.ОбнаруженныйМетаданные) Тогда
			Причина.ОбнаруженныйТип = Тип("КонстантаМенеджерЗначения." + СтрокаТаблицы.ОбнаруженныйМетаданные.Имя);
		Иначе
			Причина.ОбнаруженныйСсылка = СтрШаблон(
				НСтр("ru = 'Обнаружены неразрешимые ссылки (%1)'"),
				СтрокаТаблицы.ОбнаруженныйМетаданные.Представление());
			Причина.ОбнаруженныйТип = Тип("Строка");
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	// Регистрация информации об объектах метаданных (если требуется).
	ОбнаруженныйИнформация = СформироватьИнформациюОТипах(ПараметрыВыполнения, Причина.ОбнаруженныйТип);
	
	// Заполнение подчиненных полей.
	Если ОбнаруженныйИнформация.Ссылочный Тогда
		Причина.ОбнаруженныйПометкаУдаления = УИ_ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Причина.ОбнаруженныйСсылка,
			"ПометкаУдаления");
	Иначе
		Причина.ОбнаруженныйПометкаУдаления = Ложь;
	КонецЕсли;
КонецПроцедуры

// Основная механика удаления помеченных объектов.
Функция ИзвлечьРезультат(ПараметрыВыполнения)
	РезультатУдаления = ПараметрыВыполнения;
	РезультатУдаления.Удалить("ВсеПомеченныеНаУдаление");
	РезультатУдаления.Удалить("ВсеПомеченныеНаУдаление");
	Возврат РезультатУдаления;
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Передача информации на клиент.

// Инициализирует структуру параметров, необходимых для передачи на клиент.
Процедура ИнициализироватьПараметрыДляРегистрацииПрогресса(ПараметрыВыполнения)
	Если Не ПараметрыВыполнения.Интерактивное Тогда
		Возврат;
	КонецЕсли;

	ПараметрыВыполнения.Вставить("ДостигнутыйПроцент", 0);
	ПараметрыВыполнения.Вставить("Диапазон", 0);
	ПараметрыВыполнения.Вставить("СледующийКонтрольныйНомер", 0);
	ПараметрыВыполнения.Вставить("Номер", 0);
	ПараметрыВыполнения.Вставить("Всего", 0);
	ПараметрыВыполнения.Вставить("Время", ТекущаяДатаСеанса() - 0.1);

	ПараметрыВыполнения.Вставить("Диапазоны", Новый Соответствие);

	ОбщийВес = 0;
	Если ПараметрыВыполнения.ИскатьПомеченные Тогда
		ПараметрыВыполнения.Диапазоны.Вставить("ПередПоискомПомеченныхНаУдаление", 5);
		ПараметрыВыполнения.Диапазоны.Вставить("ПоискПомеченныхНаУдаление", 4);
		ПараметрыВыполнения.Диапазоны.Вставить("ВсеПомеченныеНаУдаление", 1);
		ОбщийВес = ОбщийВес + 10;
	КонецЕсли;
	Если ПараметрыВыполнения.УдалятьПомеченные Тогда
		Если ПараметрыВыполнения.Монопольно Тогда
			ПараметрыВыполнения.Диапазоны.Вставить("МонопольноеУдаление", 80);
			ПараметрыВыполнения.Диапазоны.Вставить("ПрепятствующиеУдалению", 10);
		Иначе // Не монопольно.
			ПараметрыВыполнения.Диапазоны.Вставить("ТехнологическиеОбъекты", 10);
			ПараметрыВыполнения.Диапазоны.Вставить("ПользовательскиеОбъекты", 70);
			ПараметрыВыполнения.Диапазоны.Вставить("ПовторноУдаляемые", 10);
		КонецЕсли;
		ОбщийВес = ОбщийВес + 90;
	КонецЕсли;
	Если ОбщийВес <> 0 И ОбщийВес <> 100 Тогда
		Коэффициент = 100 / ОбщийВес;
		Для Каждого КлючИЗначение Из ПараметрыВыполнения.Диапазоны Цикл
			ПараметрыВыполнения.Диапазоны.Вставить(КлючИЗначение.Ключ, Окр(КлючИЗначение.Значение * Коэффициент, 0));
		КонецЦикла;
	КонецЕсли;

КонецПроцедуры

// Регистрирует начало процесса.
Процедура ОтметитьНачалоОбходаКоллекции(ПараметрыВыполнения, ИмяКоллекции, Коллекция = Неопределено)
	Если Не ПараметрыВыполнения.Интерактивное Тогда
		Возврат;
	КонецЕсли;
	ПараметрыВыполнения.ДостигнутыйПроцент = ПараметрыВыполнения.ДостигнутыйПроцент + ПараметрыВыполнения.Диапазон;
	ПараметрыВыполнения.Диапазон = ПараметрыВыполнения.Диапазоны[ИмяКоллекции];
	ПараметрыВыполнения.СледующийКонтрольныйНомер = 0;
	ПараметрыВыполнения.Номер = 0;

	Если Коллекция <> Неопределено Или ПараметрыВыполнения.Свойство(ИмяКоллекции, Коллекция) Тогда
		ПараметрыВыполнения.Всего = Коллекция.Количество();
	Иначе
		ПараметрыВыполнения.Всего = 1;
		ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, ИмяКоллекции);
	КонецЕсли;
КонецПроцедуры

// Регистрирует прогресс.
Процедура ОтметитьПрогрессОбходаКоллекции(ПараметрыВыполнения, ИмяКоллекции)
	Если Не ПараметрыВыполнения.Интерактивное Тогда
		Возврат;
	КонецЕсли;
	
	// Проверяется целесообразность передачи информации на клиент.
	Если ПараметрыВыполнения.ИскатьПомеченные Тогда
		Возврат; // Если текущий шаг - расчет помеченных, то оповещать клиент не требуется.
	КонецЕсли;
	
	// Проверяется целесообразность передачи информации на клиент.
	Если ПараметрыВыполнения.Всего < 10 Тогда
		Возврат; // Если всего обрабатывается меньше 10 объектов - то оповещать клиент не требуется.
	КонецЕсли;
	
	// Регистрация прогресса.
	ПараметрыВыполнения.Номер = ПараметрыВыполнения.Номер + 1;
	
	// Проверка что подошло время передачи информации на клиент.
	Если ТекущаяДатаСеанса() < ПараметрыВыполнения.Время Тогда
		Возврат; // Не чаще, чем раз в 3 секунды.
	КонецЕсли;
	
	// Установка следующего времени передачи информации на клиент.
	ПараметрыВыполнения.Время = ПараметрыВыполнения.Время + ПараметрыВыполнения.ПериодЗаписи;
	
	// Проверка что набралось достаточно изменений для передачи информации на клиент.
	Если ПараметрыВыполнения.Номер < ПараметрыВыполнения.СледующийКонтрольныйНомер Тогда
		Возврат; // Не чаще, чем набралось измененных объектов для изменения прогресса на 1 шаг.
	КонецЕсли;
	ШагОповещения = Цел(ПараметрыВыполнения.Всего / 100) + 1;
	ПараметрыВыполнения.СледующийКонтрольныйНомер = ПараметрыВыполнения.Номер + ШагОповещения;
	
	// Расчет информации для передачи на клиент.

	Процент = ПараметрыВыполнения.ДостигнутыйПроцент + ПараметрыВыполнения.Диапазон * ПараметрыВыполнения.Номер
		/ ПараметрыВыполнения.Всего;
	
	// Подготовка передаваемых параметров.
	Если ИмяКоллекции = "ПередПоискомПомеченныхНаУдаление" Тогда

		Текст = НСтр("ru = 'Подготовка к поиску объектов, помеченных на удаление.'");

	ИначеЕсли ИмяКоллекции = "НайтиПомеченныеНаУдаление" Тогда

		Текст = НСтр("ru = 'Поиск объектов, помеченных на удаление.'");

	ИначеЕсли ИмяКоллекции = "ВсеПомеченныеНаУдаление" Тогда

		Текст = НСтр("ru = 'Анализ помеченных на удаление.'");

	ИначеЕсли ИмяКоллекции = "ТехнологическиеОбъекты" Тогда

		Текст = НСтр("ru = 'Подготовка к удалению.'");

	ИначеЕсли ИмяКоллекции = "МонопольноеУдаление" Тогда

		Текст = НСтр("ru = 'Выполняется удаление объектов.'");

	ИначеЕсли ИмяКоллекции = "ПользовательскиеОбъекты" Тогда

		НеУдалено = ПараметрыВыполнения.НеУдаленные.Количество();
		ПредставлениеНомер     = Формат(ПараметрыВыполнения.Номер, "ЧН=0; ЧГ=");
		ПредставлениеВсего     = Формат(ПараметрыВыполнения.Всего, "ЧН=0; ЧГ=");
		ПредставлениеНеУдалено = Формат(НеУдалено, "ЧН=0; ЧГ=");
		Если НеУдалено = 0 Тогда // Переход на СтрШаблон невозможен.
			Текст = НСтр("ru = 'Удалено: %1 из %2 объектов.'");
			Текст = СтрШаблон(Текст, ПредставлениеНомер, ПредставлениеВсего);
		Иначе
			Текст = НСтр("ru = 'Обработано: %1 из %2 объектов, из них не удалено: %3.'");
			Текст = СтрШаблон(Текст, ПредставлениеНомер, ПредставлениеВсего, ПредставлениеНеУдалено);
		КонецЕсли;

	ИначеЕсли ИмяКоллекции = "ПовторноУдаляемые" Тогда

		Текст = СтрШаблон(
			НСтр("ru = 'Повторная проверка не удаленных объектов: %1 из %2.'"), Формат(ПараметрыВыполнения.Номер,
			"ЧН=0; ЧГ="), Формат(ПараметрыВыполнения.Всего, "ЧН=0; ЧГ="));

	ИначеЕсли ИмяКоллекции = "ПрепятствующиеУдалению" Тогда

		Текст = СтрШаблон(
			НСтр("ru = 'Анализ объектов, препятствующих удалению: %1 из %2.'"), Формат(ПараметрыВыполнения.Номер,
			"ЧН=0; ЧГ="), Формат(ПараметрыВыполнения.Всего, "ЧН=0; ЧГ="));

	Иначе

		Возврат;

	КонецЕсли;
	
	// Регистрация сообщения для чтения из клиентского сеанса.
	УИ_ДлительныеОперации.СообщитьПрогресс(Процент, Текст);
КонецПроцедуры

#КонецОбласти

#КонецЕсли